package util

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"reflect"
	"strconv"
	"strings"
	"time"
)

func Bind(request http.Request, obj interface{}) error  {
	contentType := request.Header.Get("Content-Type")

	if strings.Contains(strings.ToLower(contentType), "application/json") {
		return BindJson(request, obj)
	}else if strings.Contains(strings.ToLower(contentType), "application/x-www-form-urlencoded") {
		return BindForm(request, obj)
	}

	return errors.New("当前Content Type暂不支持")
}

func BindJson(request http.Request, obj interface{}) error  {
	body, err := ioutil.ReadAll(request.Body)
	if err != nil  {
		return err
	}

	return  json.Unmarshal(body, obj)
}

func BindForm(request http.Request, obj interface{}) error  {
	request.ParseForm()

	return mapForm(request.Form, obj)
}

func mapForm(form map[string][]string, obj interface{}) error {
	//typeOf里面的接口要放入指针类型; 不然无法Elem
	typeFields := reflect.TypeOf(obj).Elem()
	valueFields := reflect.ValueOf(obj).Elem()

	for i := 0; i < typeFields.NumField(); i++ {
		typeField := typeFields.Field(i) //所有的字段类型列表
		valueField := valueFields.Field(i) //所有字段值列表,包含字段类型

		if !valueField.CanSet() {
			continue
		}

		valueFiledKind := valueField.Kind()
		inputFieldName := typeField.Tag.Get("form")
		if inputFieldName == "" {

			inputFieldName = typeField.Tag.Get("json")
			if inputFieldName == "" {
				runes := []rune(typeField.Name)
				s := strings.ToLower(string(runes[0]))
				inputFieldName = s + string(runes[1:])
			}
			if valueFiledKind == reflect.Struct {
				err := mapForm(form, valueField.Addr().Interface())
				if err != nil {
					continue
				}
			}
			
		}

		inputVal, exists := form[inputFieldName]
		if !exists {
			continue
		}

		numElems := len(inputVal)

		if valueFiledKind == reflect.Slice && numElems > 0 {

			kind := valueField.Type().Elem().Kind()
			slice := reflect.MakeSlice(valueField.Type(), numElems, numElems)

			for j := 0; j < numElems; j++ {
				if err := setWithProperType(kind, inputVal[j], slice.Index(j)); err != nil {
					return err
				}
			}

			valueField.Field(i).Set(slice)
		}else  {
			if _, isTime := valueField.Interface().(time.Time); isTime {
				if err := setTimeField(inputVal[0], typeField, valueField); err != nil {
					return err
				}

				continue
			}

			if err := setWithProperType(typeField.Type.Kind(), inputVal[0], valueField); err != nil {
				return err
			}

		}

	}

	return nil
}

func setWithProperType(valueKind reflect.Kind, val string, structField reflect.Value) error  {
	switch valueKind {
	case reflect.Int:
		return setIntField(val, 0, structField)
	case reflect.Int8:
		return setIntField(val, 8, structField)
	case reflect.Int16:
		return setIntField(val, 16, structField)
	case reflect.Int32:
		return setIntField(val, 32, structField)
	case reflect.Int64:
		return setIntField(val, 64, structField)
	case reflect.Uint:
		return setUintField(val, 0, structField)
	case reflect.Uint8:
		return setUintField(val, 8, structField)
	case reflect.Uint16:
		return setUintField(val, 16, structField)
	case reflect.Uint32:
		return setUintField(val, 32, structField)
	case reflect.Uint64:
		return setUintField(val, 64, structField)
	case reflect.Bool:
		return setBoolField(val,  structField)
	case reflect.Float32:
		return setFloatField(val, 32,  structField)
	case reflect.Float64:
		return setFloatField(val, 64,  structField)
	case reflect.String:
		structField.SetString(val)
	default:
		return errors.New(" Not Unknown Field Kind ")



	}
	return nil
}

func setIntField(val string, byteSize int,  structField reflect.Value) error  {
	if val == "" {
		val = "0"
	}

	parseInt, err := strconv.ParseInt(val,10,  byteSize)

	if err == nil {
		structField.SetInt(parseInt)
	}

	return err
}

func setUintField(val string, byteSize int,  structField reflect.Value) error {
	if val == "" {
		val = "0"
	}

	parseUint, err := strconv.ParseUint(val, 10, byteSize)
	if err == nil {
		structField.SetUint(parseUint)
	}

	return err
}

func setBoolField(val string,  structField reflect.Value) error  {
	if val == "" {
		val = "false"
	}

	parseBool, err := strconv.ParseBool(val)
	if err == nil {
		structField.SetBool(parseBool)
	}

	return err
}

func setFloatField(val string, byteSize int,  structField reflect.Value) error  {
	if val == "" {
		val = "0"
	}

	parseFloat, err := strconv.ParseFloat(val, byteSize)
	if err == nil {
		structField.SetFloat(parseFloat)
	}

	return err
}

func setTimeField(val string, structField reflect.StructField, value reflect.Value) error  {
	timeFormat := structField.Tag.Get("time_format")

	if timeFormat == "" {
		timeFormat = "2006-01-02 15:04:05"
		val = strings.Replace(val, "/", "-", 0)
		num := len(strings.Split(val, " "))
		if num == 1 {
			val += " 00:00:00"
		}else {
			//2018-01-02 00
			num = len(strings.Split(val, ":"))
			if num == 1 {
				val += ":00:00"
			}else if num == 2 {
				val += ":00"
			}
		}

	}

	if val == "" {
		value.Set(reflect.ValueOf(time.Time{}))
		return nil
	}

	local := time.Local


	if isUtc, _ := strconv.ParseBool(structField.Tag.Get("time_utc")); isUtc {
		local = time.UTC
	}
	if locTag := structField.Tag.Get("time_location"); locTag != "" {
		loc, err := time.LoadLocation(locTag)
		if err != nil {
			return err
		}

		local = loc
	}

	t, err := time.ParseInLocation(timeFormat, val, local)

	if err == nil {
		value.Set(reflect.ValueOf(t))
	}

	return err
}